﻿using System;
using System.Windows.Threading;

namespace Songhay.Wpf.Mvvm.Extensions
{
    /// <summary>
    /// Extension methods for WPF UI-thread dispatching.
    /// </summary>
    /// <remarks>
    /// Based on “Good practice to use Dispatcher in WPF background thread”
    /// by Eric De Carufel
    /// http://blog.decarufel.net/2009/03/good-practice-to-use-dispatcher-in-wpf.html
    /// 
    /// Example:
    /// <code>
    ///    private static void DoWork(object state)
    ///    {
    ///        AboutView win = (AboutView) state;
    ///        for (int i = 0; i &lt; 100; i++)
    ///        {
    ///            // do some work
    ///            win.ProgressBar1.Dispatch((p, v) =&gt; p.Value = v, i);
    ///        }
    ///        win.ProgressBar1.Dispatch(p =&gt; p.Value = 100);
    ///    }
    /// </code>
    /// </remarks>
    public static class DispatcherExtensions
    {
        /// <summary>
        /// Dispatches the specified source.
        /// </summary>
        /// <typeparam name="TResult">The type of the result.</typeparam>
        /// <param name="source">The source.</param>
        /// <param name="func">The func.</param>
        /// <returns></returns>
        public static TResult Dispatch<TResult>(this DispatcherObject source, Func<TResult> func)
        {
            if(source.Dispatcher.CheckAccess())
                return func();

            return (TResult)source.Dispatcher.Invoke(func);
        }

        /// <summary>
        /// Dispatches the specified source.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="TResult">The type of the result.</typeparam>
        /// <param name="source">The source.</param>
        /// <param name="func">The func.</param>
        /// <returns></returns>
        public static TResult Dispatch<T, TResult>(this T source,
            Func<T, TResult> func) where T : DispatcherObject
        {
            if(source.Dispatcher.CheckAccess())
                return func(source);

            return (TResult)source.Dispatcher.Invoke(func, source);
        }

        /// <summary>
        /// Dispatches the specified source.
        /// </summary>
        /// <typeparam name="TSource">The type of the source.</typeparam>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="TResult">The type of the result.</typeparam>
        /// <param name="source">The source.</param>
        /// <param name="func">The func.</param>
        /// <param name="param1">The param1.</param>
        /// <returns></returns>
        public static TResult Dispatch<TSource, T, TResult>(this TSource source,
            Func<TSource, T, TResult> func, T param1) where TSource : DispatcherObject
        {
            if(source.Dispatcher.CheckAccess())
                return func(source, param1);

            return (TResult)source.Dispatcher.Invoke(func, source, param1);
        }

        /// <summary>
        /// Dispatches the specified source.
        /// </summary>
        /// <typeparam name="TSource">The type of the source.</typeparam>
        /// <typeparam name="T1">The type of the 1.</typeparam>
        /// <typeparam name="T2">The type of the 2.</typeparam>
        /// <typeparam name="TResult">The type of the result.</typeparam>
        /// <param name="source">The source.</param>
        /// <param name="func">The func.</param>
        /// <param name="param1">The param1.</param>
        /// <param name="param2">The param2.</param>
        /// <returns></returns>
        public static TResult Dispatch<TSource, T1, T2, TResult>(this TSource source,
            Func<TSource, T1, T2, TResult> func, T1 param1, T2 param2) where TSource : DispatcherObject
        {
            if(source.Dispatcher.CheckAccess())
                return func(source, param1, param2);

            return (TResult)source.Dispatcher.Invoke(func, source, param1, param2);
        }

        /// <summary>
        /// Dispatches the specified source.
        /// </summary>
        /// <typeparam name="TSource">The type of the source.</typeparam>
        /// <typeparam name="T1">The type of the 1.</typeparam>
        /// <typeparam name="T2">The type of the 2.</typeparam>
        /// <typeparam name="T3">The type of the 3.</typeparam>
        /// <typeparam name="TResult">The type of the result.</typeparam>
        /// <param name="source">The source.</param>
        /// <param name="func">The func.</param>
        /// <param name="param1">The param1.</param>
        /// <param name="param2">The param2.</param>
        /// <param name="param3">The param3.</param>
        /// <returns></returns>
        public static TResult Dispatch<TSource, T1, T2, T3, TResult>(this TSource source,
            Func<TSource, T1, T2, T3, TResult> func, T1 param1, T2 param2, T3 param3) where TSource : DispatcherObject
        {
            if(source.Dispatcher.CheckAccess())
                return func(source, param1, param2, param3);

            return (TResult)source.Dispatcher.Invoke(func, source, param1, param2, param3);
        }

        /// <summary>
        /// Dispatches the specified source.
        /// </summary>
        /// <param name="source">The source.</param>
        /// <param name="func">The func.</param>
        public static void Dispatch(this DispatcherObject source, Action func)
        {
            if(source.Dispatcher.CheckAccess())
                func();
            else
                source.Dispatcher.Invoke(func);
        }

        /// <summary>
        /// Dispatches the specified source.
        /// </summary>
        /// <typeparam name="TSource">The type of the source.</typeparam>
        /// <param name="source">The source.</param>
        /// <param name="func">The func.</param>
        public static void Dispatch<TSource>(this TSource source,
            Action<TSource> func) where TSource : DispatcherObject
        {
            if(source.Dispatcher.CheckAccess())
                func(source);
            else
                source.Dispatcher.Invoke(func, source);
        }

        /// <summary>
        /// Dispatches the specified source.
        /// </summary>
        /// <typeparam name="TSource">The type of the source.</typeparam>
        /// <typeparam name="T1">The type of the 1.</typeparam>
        /// <param name="source">The source.</param>
        /// <param name="func">The func.</param>
        /// <param name="param1">The param1.</param>
        public static void Dispatch<TSource, T1>(this TSource source,
            Action<TSource, T1> func, T1 param1) where TSource : DispatcherObject
        {
            if(source.Dispatcher.CheckAccess())
                func(source, param1);
            else
                source.Dispatcher.Invoke(func, source, param1);
        }

        /// <summary>
        /// Dispatches the specified source.
        /// </summary>
        /// <typeparam name="TSource">The type of the source.</typeparam>
        /// <typeparam name="T1">The type of the 1.</typeparam>
        /// <typeparam name="T2">The type of the 2.</typeparam>
        /// <param name="source">The source.</param>
        /// <param name="func">The func.</param>
        /// <param name="param1">The param1.</param>
        /// <param name="param2">The param2.</param>
        public static void Dispatch<TSource, T1, T2>(this TSource source,
            Action<TSource, T1, T2> func, T1 param1, T2 param2) where TSource : DispatcherObject
        {
            if(source.Dispatcher.CheckAccess())
                func(source, param1, param2);
            else
                source.Dispatcher.Invoke(func, source, param1, param2);
        }

        /// <summary>
        /// Dispatches the specified source.
        /// </summary>
        /// <typeparam name="TSource">The type of the source.</typeparam>
        /// <typeparam name="T1">The type of the 1.</typeparam>
        /// <typeparam name="T2">The type of the 2.</typeparam>
        /// <typeparam name="T3">The type of the 3.</typeparam>
        /// <param name="source">The source.</param>
        /// <param name="func">The func.</param>
        /// <param name="param1">The param1.</param>
        /// <param name="param2">The param2.</param>
        /// <param name="param3">The param3.</param>
        public static void Dispatch<TSource, T1, T2, T3>(this TSource source,
            Action<TSource, T1, T2, T3> func, T1 param1, T2 param2, T3 param3) where TSource : DispatcherObject
        {
            if(source.Dispatcher.CheckAccess())
                func(source, param1, param2, param3);
            else
                source.Dispatcher.Invoke(func, source, param1, param2, param3);
        }
    }
}
